
#include "superficierev.h"

#include "coord.h"
#include "curva.h"
#include "bezier.h"

#include <cassert>


SuperficieRevolucion::SuperficieRevolucion(
      const Curva &curvaRevolucion, const Curva &curvaPerfil) :
   curvaRevolucion(curvaRevolucion),
   curvaPerfil(curvaPerfil) {}


   SuperficieRevolucion::~SuperficieRevolucion() {
   }

/*
 * Discretiza la curva de Revolucion y para cada punto discretizado
 * calcula la discretizacion (rotada) de la curva de Perfil
 *
 * La superficie estara centrada en el centro de la curva de Revolucion.
 * Su altura coincidira con la altura de la curva de Perfil, coincidiendo
 * la base del Perfil con el plano z = 0.
 *
 * Precondicion:
 *    0 <= u <= 1
 *    0 <= v <= 1
 *
 * */
Coord SuperficieRevolucion::operator()(float u, float v) const {

   // Discretizamos la curva de Revolucion segun el paso u
   // Se obtiene tanto el punto P discretizado como la direccion
   // normal P_Direccion sobre dicho punto.
   //
   // Donde la Direccion es de la forma (x, y, 0)
   //
   Coord p = curvaRevolucion(u);
   Coord p_direccion = (p - curvaRevolucion.centro()).normalizar();
   assert(p.z() == 0);
   assert(p_direccion.z() == 0);

   // Coordenada de Referencia
   Coord referencia(curvaPerfil(0.0).x(), 0.0, curvaPerfil(1.0).z());

   // Discretizamos la curva de Perfil segun el paso v
   // Se realiza una traslacion respecto a la coordenada de Referencia
   // Obteniendo 
   //    - distancia radial: distancia desde el centro de rotacion
   //                        de la curva de Revolucion hasta el punto final
   //
   //    - altura Z: Altura sobre la coordenada z
   //
   Coord b = curvaPerfil(1.0 - v) - referencia;
   assert(b.y() == 0.0);

   // distanciaRadial = b.x();
   // altura = b.z();

   p += p_direccion * b.x();
   p.z() = b.z();

   return p;
}

Coord SuperficieRevolucion::eval(float u, float v) const { 
   return this->operator()(u, v); 
}

Coord SuperficieRevolucion::normal(float u, float v) const {
   Coord q = curvaRevolucion.tangente(u);

   Coord tangente = curvaPerfil.tangente(v);
   assert(tangente.y() == 0.0);

   Coord p = (curvaRevolucion(u) - curvaRevolucion.centro());

   p *= tangente.x();
   p.z() = tangente.z();

   return p.cross(q).normalizar();
}


float SuperficieRevolucion::altura() const {
   return curvaPerfil(0.0).z() - curvaPerfil(1.0).z();
}
